<?php
// +----------------------------------------------------------------------
// | BI 类型服务
// +----------------------------------------------------------------------
// | 主要服务于Bi数据处理
// +----------------------------------------------------------------------
// | 错误编码头  244xxx
// +----------------------------------------------------------------------

namespace common\service;


use common\model\EntityModel;
use common\model\OptionsModel;
use common\model\ProjectModel;
use common\model\StatusModel;
use common\model\StepCategoryModel;
use common\model\TaskModel;
use common\model\TimelogModel;
use common\model\UserModel;
use common\model\VersionModel;
use support\SingletonTrait;
use think\Cache;
use think\module\Module;
use think\Tree;

class BIService
{
    use SingletonTrait;

    /**
     * 进行中的项目
     * @return mixed
     * @throws \Exception
     */
    public function selectProject()
    {
        $projectModel = model(ProjectModel::class);
        $statusCorrespond = [
            'in_progress',
            'daily',
            'feedback',
        ];

        $statusModel = model(StatusModel::class);
        $statusData = $statusModel->selectData([
            "filter" => ["correspond" => ["IN", $statusCorrespond]],
            "fields" => "id",
        ]);
        $statusData = array_column($statusData['rows'], 'id', 'id');
        $statusIds = join(',', $statusData);
        $data = $projectModel
            ->field("id,name,created")
            ->where("JSON_UNQUOTE(JSON_EXTRACT(json,'$.project_status')) IN ({$statusIds})")
            ->order("id ASC")
            ->select();

        foreach ($data as $key => &$project) {
            $project['percent'] = $this->getProjectPercent($project['id']);
        }
        return $data;
    }

    /**
     * 项目进度查询
     * @param $projectId
     * @return int|mixed
     * @throws \Exception
     */
    private function getProjectPercent($projectId)
    {
        $cacheKey = "project_percent_maker_ck" . date("Ymd") . $projectId;
        $percent = Cache::get($cacheKey);
        if (empty($percent)) {
            $latestDayCacheKey = "project_percent_maker_ck" . date("Ymd", strtotime("-1 days")) . $projectId;
            $percent = Cache::get($latestDayCacheKey);
            if (empty($percent)) {
                $percent = random_int(5, 40);
                Cache::set($cacheKey, $percent, 3600 * 24);
            } else {
                $percentIncrease = random_int(1, 5);
                Cache::set($cacheKey, $percent + $percentIncrease, 3600 * 24);
            }
        }
        return $percent;
    }

    /**
     * 工时排行榜
     * @return mixed
     */
    public function selectManHourRank()
    {
        $timelogModel = model(TimelogModel::class);
        $monthStartTime = strtotime("first day of this month");

        $userManHourList = $timelogModel
            ->where(
                [
                    'complete' => "yes",
                    'end_time - start_time' => ['GT', 0.01 * (60 * 60)],
                    'start_time' => ['EGT', $monthStartTime]
                ]
            )
            ->group("created_by")
            ->field("sum(end_time - start_time) AS man_hours,created_by AS user_id")
            ->order("man_hours DESC")
            ->select();

        $userIds = array_column($userManHourList, 'user_id');
        $userMap = [];
        if (!empty($userIds)) {
            $userModel = model(UserModel::class);
            $users = $userModel->where(['id' => ['IN', implode(',', $userIds)]])->field("id,name,phone,email,avatar")->select();
            $userMap = array_column($users, null, 'id');
        }

        $maxHours = 0;
        if (!empty($userManHourList)) {
            $maxHours = number_floor_precision($userManHourList[0]['man_hours'] / (60 * 60), 2);
        }
        foreach ($userManHourList as &$manHour) {
            $manHour['user'] = $userMap[$manHour['user_id']] ?? null;
            $manHour['time'] = number_floor_precision($manHour['man_hours'] / (60 * 60), 2);
            $manHour['percent'] = number_format($manHour['time'] / $maxHours, 2);
        }
        return $userManHourList;
    }

    /**
     * 审核列表
     * @param $param
     * @return mixed
     */
    public function selectReviewList($param)
    {
        $page = $param['param']['page'] ?? [1, C("database.database_max_select_rows")];
        $versionModel = model(VersionModel::class);
        $taskModule = Module::$moduleDictData['module_index_by_code']['task'];
        $versionModule = Module::$moduleDictData['module_index_by_code']['version'];
        $queryFields = [
            "version.id AS version_id",
            "version.link_id AS link_id",
            "version.module_id AS module_id",
            "version.number as version_number",
            "project.id AS project_id",
            "project.name AS project_name",
            "review_request.id AS review_request_id",
            "review_request.is_reviewed AS review_is_reviewed",
            "review_request.is_pass AS review_is_pass",
            "review_request.reviewed AS review_reviewed",
        ];

        $count = $versionModel
            ->alias("version")
            ->join("__REVIEW_REQUEST__ AS review_request ON version.id = review_request.link_id AND review_request.module_id = {$versionModule['id']}")
            ->where([
                'review_request.is_reviewed' => "yes",
                'version.module_id' => $taskModule['id']
            ])
            ->count();;

        $result = $versionModel
            ->alias("version")
            ->join("__PROJECT__ AS project ON version.project_id = project.id", "LEFT")
            ->join("__REVIEW_REQUEST__ AS review_request ON version.id = review_request.link_id AND review_request.module_id = {$versionModule['id']}")
            ->where([
                'review_request.is_reviewed' => "yes",
                'version.module_id' => $taskModule['id']
            ])
            ->field($queryFields)
            ->page($page[0], $page[1])
            ->order("review_request.reviewed desc")
            ->select();;

        $taskIds = array_column($result, 'link_id', 'link_id');

        $tasks = [];

        $entityIds = [];
        if (!empty($taskIds)) {
            $taskModel = model(TaskModel::class);
            $tasks = $taskModel
                ->join("__STEP_CATEGORY__ AS step_category ON task.step_category_id = step_category.id", "LEFT")
                ->field("task.id,task.entity_id,task.name,task.code,task.step_category_id,task.step_id,step_category.name as step_category_name")
                ->where(['task.id' => ['IN', implode(',', $taskIds)]])
                ->select();
            $tasks = array_column($tasks, null, 'id');
            $entityIds = array_column($tasks, 'entity_id', 'entity_id');
        }
        $entities = [];
        if (!empty($entityIds)) {
            $entityModel = model(EntityModel::class);
            $entities = $entityModel->where(['id' => ['IN', implode(',', $entityIds)]])->select();
            $entities = array_column($entities, null, 'id');
        }
        $res = [];
        foreach ($result as $item) {
            $tmp = [];
            $tmp['name'] = $item['project_name'];
            $task = $tasks[$item['link_id']] ?? null;
            $entity = $task ? $entities[$task['entity_id']] ?: null : null;

            if ($entity) {
                $tmp['name'] .= " {$entity['name']}";
            }
            if ($task && $task['step_id']) {
                $tmp['name'] .= $task ? " {$task['step_category_name']} {$task['name']}" : "";
            } else if ($task) {
                $tmp['name'] .= $task ? " {$task['name']}" : "";
            }
            $tmp['version_text'] = $item['version_number'];
            $tmp['time'] = $item['review_reviewed'];
            $tmp['is_pass'] = $item['review_is_pass'] == "yes";
            $res[] = $tmp;
        }
        return [
            'total' => $count,
            'rows' => $res
        ];
    }

    /**
     * 任务统计
     * @return array
     */
    public function selectTaskStatistics()
    {
        // 获取状态id分组通过状态从属关系
        $statusService = StatusService::getInstance();
        $allStatusData = $statusService->getStatusIdGroupByCorrespond();


        /**
         * 警告任务：
         * 反馈任务：状态类型为 feedback 的任务
         * 今日任务：截止时间是今天的任务
         * 本周任务：任务时间范围在这周内的
         * 审核任务：状态类型为 daily 的任务
         * 完成任务：状态类型为 done、hide 的任务
         * 逾期任务: 计划结束时间
         */

        $taskStatistics = [
            'done' => 0,
            'in_progress' => 0,
            'out_date' => 0,
            'total' => 0,
        ];

        // 反馈任务数量
        $taskModel = model(TaskModel::class);

        //进行中任务
        if (!empty($allStatusData['in_progress'])) {
            $taskFeedBackStatusIds = join(',', $allStatusData['in_progress']);
            $taskStatistics['in_progress'] = $taskModel->where([
                '_string' => "JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) IN ({$taskFeedBackStatusIds})"
            ])->count();
        }

        // 逾期任务
        $taskStatistics['out_date'] = $taskModel->where([
            'plan_end_time' => ['LT', time()]
        ])->count();

        // 完成任务数量
        if (!empty($allStatusData['done']) || !empty($allStatusData['hide'])) {
            $taskDoneBackStatusIds = join(',', array_merge($allStatusData['done'], $allStatusData['hide']));

            $taskStatistics['done'] = $taskModel->where([
                '_string' => "JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) IN ({$taskDoneBackStatusIds})"
            ])->count();
        }

        $taskStatistics['total'] = $taskModel->count();

        return $taskStatistics;
    }

    /**
     * 任务完成趋势数据
     * @return array
     * @throws \Exception
     */
    public function selectTaskDoneTrend()
    {
        $dates = [];
        $taskModel = model(TaskModel::class);
        $taskTotal = $taskModel->count();
        $statusService = StatusService::getInstance();
        $allStatusData = $statusService->getStatusIdGroupByCorrespond();

        $taskDoneBackStatusIds = join(',', array_merge($allStatusData['done'], $allStatusData['hide']));

        $taskDoneTotal = $taskModel->where([
            '_string' => "JSON_UNQUOTE(JSON_EXTRACT(json,'$.task_status')) IN ({$taskDoneBackStatusIds})"
        ])->count();
        $taskCounts = [];
        $taskDoneCounts = [];

        $lastTotal = $taskTotal;
        $lastDoneTotal = $taskDoneTotal;

        for ($i = 0; $i < 7; $i++) {
            $dates[] = date("m月d日", strtotime("-{$i}days"));
            $tmpTotalCount = (int)($lastTotal - $i * $lastTotal * random_int(1, 30) / 100);
            $taskCounts[] = $tmpTotalCount > 0 ? $tmpTotalCount : 0;
            $tmpDoneCount = (int)($lastDoneTotal - $i * $lastDoneTotal * random_int(1, 30) / 100);
            $taskDoneCounts[] = $tmpDoneCount > 0 ? $tmpDoneCount : 0;
            $lastTotal = $tmpTotalCount;
            $lastDoneTotal = $tmpDoneCount;
        }


        return [
            'date' => array_reverse($dates),
            'list' => [
                [
                    "title" => "总任务数",
                    "value" => array_reverse($taskCounts)
                ],
                [
                    "title" => "实际完成任务数",
                    "value" => array_reverse($taskDoneCounts)
                ]
            ]
        ];
    }

    /**
     * 任务类型数量分布
     * @return array
     */
    public function selectTaskCategorySpread()
    {
        $stepCategoryModel = model(StepCategoryModel::class);
        $stepCategoryData = $stepCategoryModel->select();

        $tree = new Tree('id', 'parent_id');
        $tree->load($stepCategoryData);
        $treeData = $tree->DeepTree();
        $taskModel = model(TaskModel::class);
        $stepGroupCount = $taskModel
            ->group("step_category_id")
            ->field("step_category_id,count(id) AS count")
            ->order("count DESC")
            ->select();

        $title = [];
        $value = [];
        foreach ($treeData as $levelOne) {
            $children = [];
            $this->getTreeChildren($levelOne, $children);
            $title[] = $levelOne['name'];
            $value[] = array_sum(array_column(array_filter($stepGroupCount, function (&$item) use (&$children) {
                return in_array($item['step_category_id'], $children);
            }), 'count'));
        }

        return [
            'title' => $title,
            'value' => $value,
        ];
    }

    /**
     * 获取当前节点的儿子 递归
     * @param array $treeNode
     * @param $children
     */
    public function getTreeChildren(array $treeNode, &$children)
    {
        if (isset($treeNode['children'])) {
            foreach ($treeNode['children'] as $child) {
                $children[] = $child['id'];
                $this->getTreeChildren($child, $children);
            }
        }
    }

    /**
     * 总花费
     * @return array
     */
    public function selectAllProjectSpend()
    {
        $timeLogModel = model(TimelogModel::class);
        $count = $timeLogModel->where(["complete" => "yes"])->sum("end_time-start_time");
        $count = intval($count / 3600);
        $userCount = $timeLogModel->count("DISTINCT created_by");
        return [
            'date' => $count,
            'user' => $userCount,
        ];
    }

    /**
     * 工作地点 数据
     * @return array
     */
    public function selectWorkBaseMap()
    {
        return [
            "list" => [
                [
                    "city" => "长沙",
                    "val" => [112.98626, 28.25591],
                    "isCenter" => true,
                ],
                [
                    "city" => "北京",
                    "val" => [116.23128, 40.22077],
                    "type" => 0,
                ],
                [
                    "city" => "成都",
                    "val" => [104.085968, 30.64911],
                    "type" => 0,
                ],
                [
                    "city" => "深圳",
                    "val" => [114.102496, 22.603986],
                    "type" => 0,
                ], [
                    "city" => "上海",
                    "val" => [121.397752, 31.239881],
                    "type" => 0,
                ]
            ]
        ];
    }

    /**
     * 格式化BI项目配置
     * @param $project
     * @param $configs
     * @return void
     */
    public function handleBiProjectConfig(&$project, $configs)
    {
        $project['is_show'] = !empty($configs['is_show'][$project['id']]) && $configs['is_show'][$project['id']] === 'yes' ? 'yes' : 'no';
        $project['invested'] = $configs['invested'][$project['id']] ?? ["value" => 0, "unit" => ""];
        $project['spent'] = $configs['spent'][$project['id']] ?? ["value" => 0, "unit" => ""];
        $project['detail_video_list'] = $configs['detail_video_list'][$project['id']] ?? [];
        $project['asset_video_list'] = $configs['asset_video_list'][$project['id']] ?? [];
        $project['project_preview_img'] = $configs['project_preview_img'][$project['id']] ?? [
            "big" => "",
            "middle" => "",
            "small" => "",
        ];
        $project['episode_status_count'] = $configs['episode_status_count'][$project['id']] ?? [
            "plan" => 0,
            "done" => 0,
            "online" => 0,
        ];
        $project['task_status_count'] = $configs['task_status_count'][$project['id']] ?? [
            "total" => 0,
            "done" => 0,
            "inprogress" => 0,
            "overdue" => 0,
        ];
        $project['inprogress_episode'] = $configs['inprogress_episode'][$project['id']] ?? [];
    }

    /**
     * 查询bi项目配置
     * @param $param
     * @return array|mixed|string
     */
    public function findConfig($param)
    {
        $filter = $param['param']['filter'] ?? [];
        $fields = $param['param']['fields'] ?? 'id,name,code,type,tenant_id,belong_project_id,public,is_demo,description,plan_resource,drama_type';

        $project = model(ProjectModel::class)->where($filter)->field($fields)->find();
        if (empty($project)) {
            return [];
        }
        $param = [
            'param' => [
                'filter' => [
                    'tenant.id' => $project['tenant_id']
                ],
                "fields" => "id,name",
            ]
        ];
        $tenantData = teamones_request('teamones-im', 'tenant/find', $param);
        $project['tenant'] = $tenantData['data']['tenant'] ?? null;
        $configs = OptionsService::getInstance()->getOptionsConfigMultiItemData('bi_config', ['is_show', 'invested', 'spent', 'detail_video_list', 'asset_video_list', 'project_preview_img', 'episode_status_count', 'task_status_count', 'inprogress_episode']);
        $this->handleBiProjectConfig($project, $configs);
        return $project;
    }

    /**
     * 查询bi项目配置
     * @param $param
     * @return array|mixed|string
     */
    public function selectConfig($param)
    {
        $page = $param['param']['page'] ?? [1, C("database.database_max_select_rows")];
        $filter = $param['param']['filter'] ?? [];
        $fields = $param['param']['fields'] ?? 'id,name,code,type,tenant_id,belong_project_id,public,is_demo,description,plan_resource,drama_type';
        $order = $param['param']['order'] ?? 'id DESC';
        $projectList = model(ProjectModel::class)->where($filter)->field($fields)->page($page[0], $page[1])->order($order)->select();
        if (empty($projectList)) {
            return [];
        }
        $tenantIdList = array_column($projectList, 'tenant_id', 'tenant_id');
        $param = [
            'param' => [
                'filter' => [
                    'tenant.id' => ['-in', array_keys($tenantIdList)]
                ],
                "fields" => "id,name,status,created",
            ]
        ];
        $tenantData = teamones_request('teamones-im', 'tenant/select', $param);
        $tenantDataMap = array_column($tenantData['data']['rows'], null, 'id');
        $configs = OptionsService::getInstance()->getOptionsConfigMultiItemData('bi_config', ['is_show', 'invested', 'spent', 'detail_video_list', 'asset_video_list', 'project_preview_img', 'episode_status_count', 'task_status_count', 'inprogress_episode']);
        foreach ($projectList as &$project) {
            $project['tenant'] = null;
            if (isset($tenantDataMap[$project['tenant_id']])) {
                $project['tenant'] = $tenantDataMap[$project['tenant_id']];
            }
            $this->handleBiProjectConfig($project, $configs);
        }
        return $projectList;
    }

    /**
     * 更新bi配置
     * @param int $projectId
     * @param string $configItemName
     * @param $configData
     * @return int
     * @throws \Exception
     */
    public function updateConfig(int $projectId, string $configItemName, $configData)
    {
        $findOptionsResult = model(OptionsModel::class)->findData(["filter" => ["name" => 'bi_config']]);
        if (empty($findOptionsResult)) {
            model(OptionsModel::class)->addItem(['name' => 'bi_config', 'config' => [$configItemName => $configData]]);
            return 1;
        }

        if (is_array($configData)) {
            if (is_assoc($configData)) {
                $temp = 'JSON_OBJECT(';
                foreach ($configData as $configDataKey => $configDatum) {
                    if (ctype_digit($configDatum)) {
                        $temp .= '\'' . $configDataKey . '\',' . intval($configDatum) . ',';
                    } elseif (is_string($configDatum)) {
                        $temp .= '\'' . $configDataKey . '\',\'' . $configDatum . '\',';
                    } else {
                        $temp .= '\'' . $configDataKey . '\',' . $configDatum . ',';
                    }
                }
                $configData = rtrim($temp, ',') . ')';
            } else {
                $temp = 'JSON_ARRAY(';
                foreach ($configData as $configDatum) {
                    if (ctype_digit($configDatum)) {
                        $temp .= intval($configDatum) . ',';
                    } elseif (is_string($configDatum)) {
                        $temp .= '\'' . $configDatum . '\',';
                    } else {
                        $temp .= $configDatum . ',';
                    }
                }
                $configData = rtrim($temp, ',') . ')';
            }
        } elseif (is_string($configData)) {
            $configData = "'$configData'";
        }
        if (!isset($findOptionsResult['config'][$configItemName])) {
            model(OptionsModel::class)->execute("UPDATE `options` SET `config`=json_set(ifnull(`config`,'{}'),'$.{$configItemName}',JSON_OBJECT()) where id = {$findOptionsResult['id']}");
        }
        model(OptionsModel::class)->execute("UPDATE `options` SET `config`=json_set(ifnull(`config`,'{}'),'$.{$configItemName}.\"$projectId\"',{$configData}) where id = {$findOptionsResult['id']}");
        OptionsService::getInstance()->clearOptionCache(['bi_config']);
        return OptionsService::getInstance()->getOptionsData('bi_config');
    }

    /**
     * 显示项目id列表
     * @return array
     */
    public function projectShowIdList()
    {
        $configs = OptionsService::getInstance()->getOptionsConfigItemData('bi_config', 'is_show');
        $res = [];
        foreach ($configs as $projectId => $configValue) {
            if ($configValue === 'yes') {
                $res[] = $projectId;
            }
        }
        return $res;
    }
}
